package com.joinway.framework.core.controller;

import java.sql.ResultSet;
import java.util.List;
import java.util.Optional;

import javax.servlet.http.HttpServletRequest;
import javax.xml.bind.ValidationException;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import com.joinway.framework.bean.constant.ErrorCodeConstants;
import com.joinway.framework.bean.exception.InternalException;
import com.joinway.framework.bean.exception.SessionExpiredException;
import com.joinway.framework.bean.view.View;
import com.joinway.framework.core.bean.domain.ExceptionConfig;
import com.joinway.framework.core.repository.FrameworkRepository;
import com.joinway.framework.core.utils.AppEnv;
import com.joinway.framework.data.jdbc.repository.TableRepository;

public abstract class ExceptionController {

	private final static Logger log = LoggerFactory.getLogger(ExceptionController.class);
	
	@Autowired(required=false) protected TableRepository tableRepository;
	
	@Autowired protected FrameworkRepository repository;
	
    @ExceptionHandler(InternalException.class)
    @ResponseBody
    public View handleInternalException(InternalException e, HttpServletRequest request) {
        log.warn("internal error", e);
        
        return createAppView(e);
    }

    @ExceptionHandler(SessionExpiredException.class)
    @ResponseBody
    public View handleSessionExpiredException(SessionExpiredException e, HttpServletRequest request) {
        log.warn("session expired error {}", e.getMessage());
        
        return createAppView(e);
    }

    @ExceptionHandler({MethodArgumentNotValidException.class, ValidationException.class})
    @ResponseBody
    public View handleValidationException(Exception e, HttpServletRequest request){
    	log.warn("method argument error", e);
    	
    	return createAppView(e);
    }
    
    @ExceptionHandler(Exception.class)
    @ResponseBody
    public View handle(Exception e, HttpServletRequest request) {
    	if(e.getClass().getName().equals("org.apache.catalina.connector.ClientAbortException")){
    		log.warn("client abort exception occured {}", e.getMessage());
    	}else{
    		log.error("fatal error", e);
    	}
        return createAppView(e);
    }
    
	View createAppView(Exception e){
		View view = new View();
		
		String code;
		String description;
		
		if(e instanceof InternalException){
			InternalException ie = (InternalException)e;
			code = ie.getCode();
			description = ie.getDescription();
		}else if(e instanceof MethodArgumentNotValidException || e instanceof ValidationException){
			code = ErrorCodeConstants.INVALID_INPUT;
			description = ErrorCodeConstants.INVALID_INPUT_DESC;
		}else{
			code = ErrorCodeConstants.INTERNAL_ERROR;
			description = ErrorCodeConstants.INTERNAL_ERROR_DESC;
		}
		
		view.setCode(code);
		
		String message = getDescription(code, e, description);
		view.setMessage(message);
		
		view.setError(e.getMessage());
		
		return view;
	}
	
    String getDescription(String code, Exception e, String defaultText) {
		String desc = "";
		
		List<ExceptionConfig> configs = null;
		try {
//			List<?> list = tableRepository.find("SHOW TABLES LIKE 'exception_config'");
			
			String table = "exception_config";
			ResultSet rs = tableRepository.getDataSource().getConnection().getMetaData().getTables(null, null, table, null);
			if(rs.next()){
				configs = repository.getExceptionConfig();
			}else{
				log.warn("{} doesn't exists, ignore load", table);
			}
		} catch (Exception e1) {
			log.warn("failed to get exeption configs {}", e1.getMessage());
		}
		
		Optional<ExceptionConfig> optional = null;
		
		if(CollectionUtils.isNotEmpty(configs)){
			optional = configs.stream()
					.filter(c -> c.getCode().equals(code) && c.getAppId().equals(AppEnv.getAppId())).findFirst();
		}
		
		if(optional != null && optional.isPresent()){
			desc = optional.get().getText();
		}else{
			log.warn("use raw error message as description");
			desc = StringUtils.isNotBlank(defaultText) ? defaultText : e.getMessage();
		}
		
    	return StringUtils.isBlank(desc) ? defaultText : desc;
    }
}
